package org.aphelie.torg.ruler.internal.core.text.character;

import java.io.PrintWriter;

import org.aphelie.torg.ruler.core.IModel;
import org.aphelie.torg.ruler.core.IModelChangeProvider;
import org.aphelie.torg.ruler.core.IModelChangedEvent;
import org.aphelie.torg.ruler.core.ModelChangedEvent;
import org.aphelie.torg.ruler.core.character.ICharacterBase;
import org.aphelie.torg.ruler.core.character.ICharacterModelBase;
import org.aphelie.torg.ruler.core.character.ICharacterObject;
import org.aphelie.torg.ruler.core.character.ISharedCharacterModel;

import org.aphelie.torg.ruler.internal.core.character.IWritableDelimiter;
import org.aphelie.torg.ruler.internal.core.text.DocumentElementNode;
import org.aphelie.torg.ruler.internal.core.text.IDocumentAttributeNode;
import org.aphelie.torg.ruler.internal.core.text.IDocumentElementNode;
import org.aphelie.torg.ruler.internal.core.text.IDocumentRange;
import org.aphelie.torg.ruler.internal.core.text.IEditingModel;
import org.aphelie.torg.ruler.internal.core.util.TGRXMLHelper;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.TextUtilities;


public class CharacterObjectNode extends DocumentElementNode implements ICharacterObject, IWritableDelimiter {

    private transient boolean fInTheModel;
    private transient ISharedCharacterModel fModel;

    private static final long serialVersionUID = 1L;
    private String fName;

    /**
     * 
     */
    public CharacterObjectNode() {
        super();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.pde.core.plugin.ICharacterObject#getModel()
     */
    public ISharedCharacterModel getModel() {
        return fModel;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.pde.core.plugin.ICharacterObject#getCharacterModel()
     */
    public ICharacterModelBase getCharacterModel() {
        return (ICharacterModelBase) fModel;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.pde.core.plugin.ICharacterObject#getName()
     */
    public String getName() {
        return fName;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.pde.core.plugin.ICharacterObject#isInTheModel()
     */
    public boolean isInTheModel() {
        return fInTheModel;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.pde.core.plugin.ICharacterObject#getTranslatedName()
     */
    public String getTranslatedName() {
        return getResourceString(getName());
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.pde.core.plugin.ICharacterObject#getParent()
     */
    public ICharacterObject getParent() {
        return (ICharacterObject) getParentNode();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.pde.core.plugin.ICharacterObject#getCharacterBase()
     */
    public ICharacterBase getCharacterBase() {
        return fModel != null ? ((ICharacterModelBase) fModel).getCharacterBase() : null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.pde.core.plugin.ICharacterObject#getResourceString(java.lang.String)
     */
    public String getResourceString(String key) {
        return fModel != null ? fModel.getResourceString(key) : key;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.pde.core.plugin.ICharacterObject#setName(java.lang.String)
     */
    public void setName(String name) throws CoreException {
        fName = name;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.pde.core.plugin.ICharacterObject#isValid()
     */
    public boolean isValid() {
        return false;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.pde.core.IWritable#write(java.lang.String,
     *      java.io.PrintWriter)
     */
    public void write(String indent, PrintWriter writer) {
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
     */
    public Object getAdapter(Class adapter) {
        return null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.pde.core.plugin.ICharacterObject#setInTheModel(boolean)
     */
    public void setInTheModel(boolean inModel) {
        fInTheModel = inModel;
    }

    public void setModel(ISharedCharacterModel model) {
        fModel = model;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.pde.internal.ui.model.IDocumentNode#setXMLAttribute(java.lang.String,
     *      java.lang.String)
     */
    public boolean setXMLAttribute(String name, String value) {
        // Overrided by necessity - dealing with different objects
        String oldValue = getXMLAttributeValue(name);
        if (oldValue != null && oldValue.equals(value))
            return false;
        CharacterAttribute attr = (CharacterAttribute) getNodeAttributesMap().get(name);
        try {
            if (value == null)
                value = ""; //$NON-NLS-1$
            if (attr == null) {
                attr = new CharacterAttribute();
                attr.setName(name);
                attr.setEnclosingElement(this);
                attr.setModel(getModel());
                getNodeAttributesMap().put(name, attr);
            }
            attr.setValue(value == null ? "" : value); //$NON-NLS-1$
        } catch (CoreException e) {
        }
        if (fInTheModel)
            firePropertyChanged(attr.getEnclosingElement(), attr.getAttributeName(), oldValue, value);
        return true;
    }

    protected void firePropertyChanged(IDocumentRange node, String property, Object oldValue, Object newValue) {
        if (fModel.isEditable()) {
            fModel.fireModelObjectChanged(node, property, oldValue, newValue);
        }
    }

    protected void fireStructureChanged(ICharacterObject child, int changeType) {
        IModel model = getModel();
        if (model.isEditable() && model instanceof IModelChangeProvider) {
            IModelChangedEvent e = new ModelChangedEvent(fModel, changeType, new Object[] {child}, null);
            fireModelChanged(e);
        }
    }

    protected void fireStructureChanged(ICharacterObject[] children, int changeType) {
        IModel model = getModel();
        if (model.isEditable() && model instanceof IModelChangeProvider) {
            IModelChangedEvent e = new ModelChangedEvent(fModel, changeType, children, null);
            fireModelChanged(e);
        }
    }

    private void fireModelChanged(IModelChangedEvent e) {
        IModel model = getModel();
        if (model.isEditable() && model instanceof IModelChangeProvider) {
            IModelChangeProvider provider = (IModelChangeProvider) model;
            provider.fireModelChanged(e);
        }
    }

    public String getWritableString(String source) {
        return TGRXMLHelper.getWritableString(source);
    }

    protected void appendAttribute(StringBuffer buffer, String attrName) {
        appendAttribute(buffer, attrName, ""); //$NON-NLS-1$
    }

    protected void appendAttribute(StringBuffer buffer, String attrName, String defaultValue) {
        IDocumentAttributeNode attr = getDocumentAttribute(attrName);
        if (attr != null) {
            String value = attr.getAttributeValue();
            if (value != null && value.trim().length() > 0 && !value.equals(defaultValue))
                buffer.append(" " + attr.write()); //$NON-NLS-1$
        }
    }

    public String getLineDelimiter() {
        ISharedCharacterModel model = getModel();
        IDocument document = ((IEditingModel) model).getDocument();
        return TextUtilities.getDefaultLineDelimiter(document);
    }

    public void addChildNode(IDocumentElementNode child, int position) {
        super.addChildNode(child, position);
        ((ICharacterObject) child).setInTheModel(true);
    }

    public String toString() {
        return write(false);
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.core.text.plugin.CharacterDocumentNode#reconnect(org.eclipse.pde.core.plugin.ISharedCharacterModel, org.eclipse.pde.internal.core.ischema.ISchema, org.eclipse.pde.internal.core.text.IDocumentElementNode)
     */
    public void reconnect(IDocumentElementNode parent, IModel model) {
        super.reconnect(parent, model);
        // Transient field:  In The Model
        // Value set to true when added to the parent; however, serialized
        // children's value remains unchanged.  Since, reconnect and add calls
        // are made so close together, set value to true for parent and all
        // children
        fInTheModel = true;
        // Transient field:  Model
        if (model instanceof ISharedCharacterModel) {
            fModel = (ISharedCharacterModel) model;
        }
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.core.plugin.IWritableDelimeter#writeDelimeter(java.io.PrintWriter)
     */
    public void writeDelimeter(PrintWriter writer) {
        // NO-OP
        // Child classes to override
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.ui.model.IDocumentNode#getXMLAttributeValue(java.lang.String)
     */
    public String getXMLAttributeValue(String name) {
        // Overrided by necessity - dealing with different objects
        CharacterAttribute attr = (CharacterAttribute) getNodeAttributesMap().get(name);
        return attr == null ? null : attr.getValue();
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#write(boolean)
     */
    public String write(boolean indent) {
        // Used by text edit operations
        // Subclasses to override
        return ""; //$NON-NLS-1$
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.core.text.IDocumentElementNode#writeShallow(boolean)
     */
    public String writeShallow(boolean terminate) {
        // Used by text edit operations
        // Subclasses to override
        return ""; //$NON-NLS-1$
    }

    /* (non-Javadoc)
     * @see org.eclipse.pde.internal.core.text.plugin.CharacterDocumentNode#getFileEncoding()
     */
    protected String getFileEncoding() {
        if ((fModel != null) && (fModel instanceof IEditingModel)) {
            return ((IEditingModel) fModel).getCharset();
        }
        return super.getFileEncoding();
    }

}
